using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using OpenComposite.EII.Repository;
using OpenComposite.Repository.Properties;
using System.Xml.Schema;
using System.IO;
using System.Reflection;
using System.Diagnostics;
using System.Xml;
using System.Xml.Serialization;
using System.Web;
using System.ServiceModel;
using System.Threading;
using OpenComposite.HumanActivity.Contracts;

namespace OpenComposite.EII.RepositoryUIs
{
	public partial class UCHumanActivity : RepositoryItemUI
	{
		#region Initialization

		public UCHumanActivity(IServiceProvider serviceProvider)
			: base(serviceProvider)
		{
			InitializeComponent();
		}

		#endregion

		#region Public Members

		#region Properties
		public override IRepositoryItem RepositoryItem
		{
			get
			{
				save();
				return _humanact;
			}
			set
			{
				base.RepositoryItem = value;
				if ( value is Repository.HumanActivity ) {
					_humanact = (Repository.HumanActivity)value;
					load();
				}
			}
		}

		public override string TypeText
		{
			get { return Resources.sHumanActivity; }
		}

		public override string TypeDescription
		{
			get { return ""; }
		}
		#endregion

		#region Events
		#endregion

		#region Methods
		#endregion

		#endregion

		#region Private Members

		#region Variables
		private bool _isSchemaChanged = false;
		private bool _isFormDllChanged = false;

		private XmlSchema _schema = null;
		private byte[] _formdll = null;
		private Repository.HumanActivity _humanact = null;

		private AppDomain _appDomain = null;
		private Assembly _assembly = null;
		#endregion

		#region Methods
		private void load()
		{
			this.SuspendLayout();
			try {
				if ( _humanact == null ) {
					txtName.Text = "";
					txtDesc.Text = "";
					txtAssemblyName.Text = "";
					txtSchemaName.Text = "";
					txtUrl.Text = "";
					cmbForm.Items.Clear();
					return;
				}
				txtName.Text = _humanact.Name;
				txtDesc.Text = _humanact.Description;
				string[] path = _humanact.NText.Split('|');
				if ( path.Length > 1 ) {
					txtComputerName.Text = path[0];
					txtDLLPath.Text = path[1];
				} else {
					txtComputerName.Text = string.Empty;
					txtDLLPath.Text = string.Empty;
				}
				txtAssemblyName.Text = _humanact.FormAssemblyName;
				txtSchemaName.Text = _humanact.FormSchemaName;
				_schema = _humanact.FormSchema;
				txtUrl.Text = _humanact.Url.ToString();
				cmbForm.BeginUpdate();
				try {
					cmbForm.Items.Clear();
					if ( _humanact.AssemblyFormNames != null && _humanact.AssemblyFormNames.Length > 0 )
						cmbForm.Items.AddRange(_humanact.AssemblyFormNames);
				} finally {
					cmbForm.EndUpdate();
				}
				cmbForm.SelectedItem = _humanact.FormName;
			} finally {
				this.ResumeLayout(true);
			}
			_assembly = null;
			usedByHierarchyHumanActivity.RepositoryItem = this.RepositoryItem as RepositoryItemBaseEx;
		}
		private void save()
		{
			Uri url = null;
			if ( !Uri.TryCreate(txtUrl.Text, UriKind.Absolute, out url) ) {
				MessageBox.Show("Service Url is not a valid url. Please correct your input.", this.TypeText, MessageBoxButtons.OK, MessageBoxIcon.Error);
				txtUrl.Focus();
				return;
			}
			if ( _humanact == null )
				_humanact = new Repository.HumanActivity();

			_humanact.Reload();

			_humanact.Name = txtName.Text;
			_humanact.Description = txtDesc.Text;
			_humanact.NText = string.Format("{0}|{1}", txtComputerName.Text, txtDLLPath.Text);
			_humanact.FormAssemblyName = txtAssemblyName.Text;
			_humanact.FormName = cmbForm.SelectedItem != null ? cmbForm.SelectedItem.ToString() : "";
			_humanact.FormSchemaName = txtSchemaName.Text;
			//_humanact.Url = url;

			List<string> forms = new List<string>();
			foreach ( object item in cmbForm.Items ) {
				forms.Add(item.ToString());
			}
			_humanact.AssemblyFormNames = forms.ToArray();

			if ( _isFormDllChanged ) _humanact.FormDLL = _formdll;
			if ( _isSchemaChanged ) _humanact.FormSchema = _schema;

			if ( _isSchemaChanged || _isFormDllChanged ) {
				DataTable dt = Global.Data.GetHumanActivityUpdateServiceGuid();
				if ( dt != null && dt.Rows.Count > 0 ) {
					DataRow row = dt.Rows[0];
					System.ServiceModel.ChannelFactory<IHumanActivityDesignerService> factory = null;
					try {
						factory = new ChannelFactory<IHumanActivityDesignerService>(new WSHttpBinding());
						Uri uri = new Uri(txtUrl.Text);
						if ( uri != null ) {
							int newPort = uri.Port + 2;
							string sUri = string.Format("http://{0}:{1}/HumanActivityUpdate", uri.Host, newPort);
							factory.Endpoint.Address = new EndpointAddress(sUri);
						} else {
							// OLD: does not work when user manually sets host
							factory.Endpoint.Address = new EndpointAddress(row["config_value"].ToString());
						}
						IHumanActivityDesignerService updatesvc = factory.CreateChannel();
						updatesvc.Update((Guid)row["config_guid"], _humanact.ID);
					} catch ( Exception ex ) {
						MessageBox.Show("Human Activity Service was not available. The new human activity will be available after the next Human Activity Service update cycle.", "Human Activity Service", MessageBoxButtons.OK, MessageBoxIcon.Information);
						Debug.WriteLine(ex);
						Debug.WriteLine("Row: " + row.ToString());
					} finally {
						try { factory.Close(); } catch { Debug.WriteLine("Error closing HumanActivityUpdate channel."); }
					}
				}
			}
			_isFormDllChanged = false;
			_isSchemaChanged = false;
		}

		private void readAssembly()
		{
			this.SuspendLayout();
			try {
				if ( _assembly == null ) {
					// clear textboxes and combobox
					txtAssemblyName.Text = "";
					txtSchemaName.Text = "";
					cmbForm.Items.Clear();
					return;
				}
				txtAssemblyName.Text = _assembly.GetName().Name;
				cmbForm.BeginUpdate();
				try {
					cmbForm.Items.Clear();
					try {
						foreach ( Type type in _assembly.GetTypes() ) {
							if ( type.BaseType != null &&
								 ( type.BaseType.Namespace == "OpenComposite.Client.Smart.Forms" &&
								   type.BaseType.Name == "frmBase2" ) ||
								 ( type.BaseType.Namespace == "OpenComposite.Client.Smart.Forms" &&
								   type.BaseType.Name == "FormHumanActivityBase" ) ) {
								cmbForm.Items.Add(type.FullName);
							}
						}
					} catch ( ReflectionTypeLoadException rtlex ) {
						TypeLoadException tlex = rtlex.LoaderExceptions.Length > 0 ? rtlex.LoaderExceptions[0] as TypeLoadException : null;
						if ( tlex != null && tlex.TypeName == "OpenComposite.Client.Smart.Forms.frmBase2" ) {
							MessageBox.Show("Please update the Human Activity Form so that it uses FormHumanActivityBase instead of frmBase2 as base class.",
								"Unsupported Human Activity detected!", MessageBoxButtons.OK, MessageBoxIcon.Error);
						} else {
							MessageBox.Show("Unexpected Type Load Error occured. Please check if all dependencies for this assembly are available.", "Unexpected error", MessageBoxButtons.OK, MessageBoxIcon.Error);
						}
						txtAssemblyName.Text = "";
						_formdll = null;
						_isFormDllChanged = true;
						_assembly = null;
					}
				} catch ( Exception ex ) {
					Debug.WriteLine(ex.Message);
				} finally {
					cmbForm.EndUpdate();
				}
				if ( cmbForm.Items.Count > 0 )
					cmbForm.SelectedIndex = 0;
			} finally {
				this.ResumeLayout(true);
			}
		}
		private void getSelectedFormInfos()
		{
			if ( _assembly == null )
				return;

			string sFormName = cmbForm.SelectedItem.ToString();
			if ( string.IsNullOrEmpty(sFormName) )
				return;

			Type tForm = null;
			foreach ( Type type in _assembly.GetTypes() ) {
				if ( type.FullName == sFormName ) {
					tForm = type;
					break;
				}
			}
			if ( tForm == null )
				return;
			ConstructorInfo ctor = tForm.GetConstructor(Type.EmptyTypes);
			object objForm = null;
			try {
				objForm = ctor.Invoke(null);
			} catch {
				MessageBox.Show("An error occured while loading the HumanActivityForm.",
					"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
				return;
			}
			MethodInfo mi = tForm.GetMethod("GetFormDataType", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod);
			if ( mi != null ) {
				Type formdatatype = mi.Invoke(objForm, null) as Type;
				if ( formdatatype != null ) {
					XmlSchemas xsds = new XmlSchemas();
					XmlReflectionImporter xrefimp = new XmlReflectionImporter();
					XmlSchemaExporter e = new XmlSchemaExporter(xsds);
					e.ExportTypeMapping(xrefimp.ImportTypeMapping(formdatatype));
					if ( xsds.Count > 0 )
						_schema = xsds[0];
					else
						_schema = null;
					txtSchemaName.Text = formdatatype.FullName;
					_isSchemaChanged = true;
				}
			}
		}

		Module _assembly_ModuleResolve(object sender, ResolveEventArgs e)
		{
			return null;
		}

		#endregion

		#region Event Handlers

		private void btnUploadDLL_Click(object sender, EventArgs e)
		{
			OpenFileDialog dlg = new OpenFileDialog();
			dlg.DefaultExt = "dll";
			dlg.Filter = "Form Assembly (*.dll)|*.dll";
			dlg.FilterIndex = 1;
			//dlg.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
			dlg.Multiselect = false;
			dlg.Title = "Select Form Assembly";
			if ( string.IsNullOrEmpty(txtDLLPath.Text) && txtComputerName.Text == Environment.MachineName ) {
				dlg.InitialDirectory = Path.GetDirectoryName(txtDLLPath.Text);
			}
			if ( dlg.ShowDialog() == DialogResult.OK ) {
				string filepath = dlg.FileName;
				txtComputerName.Text = Environment.MachineName;
				txtDLLPath.Text = filepath;
				using ( Stream s = dlg.OpenFile() ) {
					s.Position = 0;
					_formdll = new byte[s.Length];
					s.Read(_formdll, 0, (int)s.Length);
					_isFormDllChanged = true;
				}
				// load assembly
				try {
					if ( _appDomain != null ) {
						AppDomain.Unload(_appDomain);
					}
					string pathtmp = Path.Combine(Path.GetTempPath(), "OpenComposite");
					if ( !Directory.Exists(pathtmp) ) Directory.CreateDirectory(pathtmp);
					using ( FileStream fs = new FileStream(Path.Combine(pathtmp, Path.GetFileName(filepath)),
														   FileMode.Create) ) {
						fs.Write(_formdll, 0, _formdll.Length);
					}

					//AppDomainSetup setup = new AppDomainSetup();
					//setup.ApplicationBase = Application.StartupPath;
					//setup.PrivateBinPath = pathtmp;

					//Assembly asm = Assembly.Load(_formdll);
					//asm.GetLoadedModules();
					//_appDomain = AppDomain.CreateDomain("AssemblyForm" + Guid.NewGuid().ToString("N"),
					//    new System.Security.Policy.Evidence(AppDomain.CurrentDomain.Evidence), setup);
					//_appDomain.Load("OpenComposite.Base");
					//_appDomain.Load("OpenComposite.Base.Data.Structures");
					//_appDomain.Load("OpenComposite.Base.Structures");
					//_appDomain.Load("OpenComposite.Base.Data");
					//_appDomain.Load("OpenComposite.Client.Smart.Controls");

					//_assembly = _appDomain.Load(Path.GetFileNameWithoutExtension(filepath));
					_assembly = Assembly.Load(_formdll);
					_assembly.ModuleResolve += new ModuleResolveEventHandler(_assembly_ModuleResolve);

					// read form informations
					readAssembly();
				} catch ( Exception ex ) {
					MessageBox.Show(ex.Message, ex.Source, MessageBoxButtons.OK, MessageBoxIcon.Error);
				}
			}
		}

		private void btnViewForm_Click(object sender, EventArgs e)
		{
			if ( _assembly == null ) {
				try {
					_formdll = _humanact.FormDLL;
					_assembly = Assembly.Load(_formdll);
				} catch { }
			}
			if ( _assembly != null ) {
				try {
					if ( cmbForm.SelectedItem == null ) return;
					Type t = _assembly.GetType(cmbForm.SelectedItem.ToString());
					if ( t != null && ( t.BaseType.Namespace == "OpenComposite.Client.Smart.Forms" ||
										 t.BaseType.Namespace == "OpenComposite.Client.Smart.Forms" ) ) {
						ConstructorInfo ctor = t.GetConstructor(Type.EmptyTypes);
						object form = ctor.Invoke(null);
						//PropertyInfo piEnabled = t.GetProperty("Enabled");
						//if ( piEnabled != null ) {
						//    piEnabled.SetValue(form, false, null);
						//}
						MethodInfo miShow = t.GetMethod("ShowDialog", Type.EmptyTypes);
						if ( miShow != null ) {
							miShow.Invoke(form, null);
						}
					}
				} catch ( TypeLoadException tlex ) {
					if ( tlex.TypeName == "OpenComposite.Client.Smart.Forms.frmBase2" ) {
						MessageBox.Show("Please update the Human Activity Form so that it uses FormHumanActivityBase instead of frmBase2 as base class",
							"Unsupported Human Activity detected!", MessageBoxButtons.OK, MessageBoxIcon.Error);
					} else {
						MessageBox.Show("Unexpected Type Load Error occured. Please check if all dependencies for this assembly are available", "Unexpected error", MessageBoxButtons.OK, MessageBoxIcon.Error);
					}
				}
			}
		}

		private void btnViewSchema_Click(object sender, EventArgs e)
		{
			if ( _schema == null )
				return;

			XmlDocument xd = new XmlDocument();
			using ( StringWriter sw = new StringWriter() ) {
				_schema.Write(sw);
				xd.LoadXml(sw.ToString());
			}
			formXmlViewer frm = new formXmlViewer();
			frm.XmlDocument = xd;
			frm.ShowDialog();
		}

		private void cmbForm_SelectedIndexChanged(object sender, EventArgs e)
		{
			getSelectedFormInfos();
		}

		private void btnEditHAHost_Click(object sender, EventArgs e)
		{
			if ( string.IsNullOrEmpty(txtUrl.Text) ) return;
			FormEditHumanActivityHostname editForm = new FormEditHumanActivityHostname();
			string url = txtUrl.Text;
			url = url.Substring(url.IndexOf("//") + 2);
			editForm.HostName = url.Substring(0, url.IndexOf(':'));
			if ( editForm.ShowDialog() == DialogResult.OK ) {
				_humanact.Url = _humanact.CreateUri(editForm.HostName);
				_humanact.Save();
				//DataSet ds = Global.Data.UserAccess.GetProcessActivity(string.Format("proc_id = {0}", _humanact.ID));
				//if ( ds != null && ds.Tables.Count > 0 ) {
				//    DataTable dt = ds.Tables[0];
				//    if ( dt.Rows.Count > 0 ) {
				//        dt.Rows[0]["activityservice_url"] = _humanact.Url;
				//    }
				//}
				txtUrl.Text = _humanact.Url.ToString();
			}
		}

		#endregion

		private void txtName_KeyPress(object sender, KeyPressEventArgs e)
		{
			if ( e.KeyChar < (char)32 ) return;
			e.Handled = !Helpers.IsValidCharForName(e.KeyChar);
		}

		#endregion

		protected override void NotifyPropertyChange(string property, object value)
		{
			if ( property == "Name" && value != null ) {
				txtName.Text = value.ToString();
			}
		}
	}
}
